<?PHP
/*
 * $Id: barge-archive.classes.php 139 2009-12-16 15:29:56Z namba0219 $
 * =============================================================================
 * Barge - Web application framework on PHP5
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 * =============================================================================
 * For more information, see http://barge.googlecode.com/
 */
namespace barge\archive;

use \Exception;
use \ZipArchive;
use \RecursiveIteratorIterator;

use barge\ClassLoader;
use barge\System;

use barge\logging\LogFactory;
use barge\io\FileInputStream;
use barge\io\FileNotFoundException;
use barge\utils\ArrayList;
use barge\utils\Bean;
use barge\utils\BeanUtils;
use barge\utils\HashMap;
use barge\utils\String;

/**
 * @author Kenji Namba
 * @version 1.0
 * @since Barge 1.0 2006/08/12
 */
abstract class Archive
{
	const ZIP = 'zip';

	/**
	 * ファイル名のエンコーディング
	 * @var string
	 */
	protected $pathEncoding = 'Shift_JIS';

	/**
	 * コンストラクタ
	 * @param string $archivename アーカイブ名
	 * @param int $arctype アーカイブタイプ
	 * @param bool $override 同名のファイルがあった場合に上書きするかどうか
	 * @return Archive
	 */
	public static function open($arcname, $arctype = null, $override = false)
	{
		if ($arctype === self::ZIP) {
			return new ZipExtArchive($arcname, $override);
		} else {
			throw new Exception('Unsupported archive type.');
		}
	}

	/**
	 * 既存のディレクトリーを追加する
	 * @param string $dirpath ディレクトリーパス
	 * @param string $zipdir
	 */
	abstract public function addDir($dirpath, $zipdir = '/');

	/**
	 * 指定されたファイルのファイルストリームを返します
	 *
	 * @return ZipInputStream
	 */
	abstract public function getInputStream($entry);

	/**
	 * ZIPアーカイブを展開します、<code>$entries</code>を指定した場合
	 * 指定ファイルまたはディレクトリーのみが展開されます
	 *
	 * @param string $distdir 展開先のディレクトリ
	 * @param string|array $entries 展開するファイル
	 */
	abstract public function extractTo($distdir, $entries = null);

	/**
	 * 新しいディレクトリーを作成し追加する
	 * @param $dirname ディレクトリー名
	 */
	abstract public function addEmptyDir($dirname);

	/**
	 * 既存のファイルをZIPファイルに追加する
	 * @param string $file 追加するファイル
	 * @param string $zippath アーカイブファイル内でのファイルパス、省略された場合は追加するファイル
	 * 		のファイル名を使用してZIPファイルのTOPレベルに格納する
	 * @throws FileNotFoundException 追加するファイルが見つからない場合にスローされる
	 */
	abstract public function addFile($file, $zippath = null);

	/**
	 * 文字列からファイルを作成し、ZIPに追加する
	 *
	 * @param string $zippath アーカイブファイル内でのファイルパス
	 * @param string $content ファイルの内容
	 */
	abstract public function addContents($zippath, $content);

	/**
	 * ストリームを閉じる
	 */
	abstract public function close();

	/**
	 * @param string $encode ファイルパスエンコーディングを設定する
	 */
	public function setPathEncoding($encode)
	{
		$this->pathEncoding = $encode;
	}

}

/**
 * @author Kenji Namba
 * @version 1.0
 * @since Barge 1.0 2006/08/12
 */
class ZipExtArchive extends Archive
{

	/**
	 * @var ZipArchive
	 */
	private $zip;


	/**
	 * コンストラクタ、すでにファイルがある場合の新規作成モードは、新しくZIPファイルを
	 * 作成するのでなく、追加モードとなるため注意
	 *
	 *
	 * @param string $arcname アーカイブ名
	 * @param bool $override 上書きモードでZIPを開くか
	 */
	public function __construct($arcname, $override = true)
	{
		$this->zip = new ZipArchive();
		if ($override) {
			$rtn = $this->zip->open($arcname, ZipArchive::CREATE | ZipArchive::OVERWRITE);
		} else {
			$rtn = $this->zip->open($arcname, ZipArchive::CREATE | ZipArchive::OVERWRITE);
		}
		if ($rtn !== true) {
			exit('zip作成に失敗！ エラーコード=' . $rtn);
		}
	}

	/**
	 * 既存のディレクトリーを追加する
	 * @param $dirpath ディレクトリーパス
	 * @param $zipdir
	 */
	public function addDir($dirpath, $zipdir = '/')
	{
		$dirIterator = new RecursiveDirectoryIterator($dirpath);
  		foreach ($iterator = new RecursiveIteratorIterator($dirIterator
            , RecursiveIteratorIterator::SELF_FIRST) as $file){
            if ($file->isDir()) {
            	$this->zip->addEmptyDir($file->getFilename());
            } else {
				$this->zip->addFile($file->getFilename());
            }
		}
	}

	/**
	 * 指定されたファイルのファイルストリームを返します
	 *
	 * @return ZipInputStream
	 */
	public function getInputStream($entry)
	{
		return new ZipInputStream($this->zip->getStream($entry));
	}

	/**
	 * ZIPアーカイブを展開します、<code>$entries</code>を指定した場合
	 * 指定ファイルまたはディレクトリーのみが展開されます
	 *
	 * @param string $distdir 展開先のディレクトリ
	 * @param string|array $entries 展開するファイル
	 */
	public function extractTo($distdir, $entries = null)
	{
		$this->zip->extractTo($distdir, $entries);
	}

	/**
	 * 新しいディレクトリーを作成し追加する
	 * @param $dirname ディレクトリー名
	 */
	public function addEmptyDir($dirname)
	{
		$this->zip->addEmptyDir(mb_convert_encoding($dirname, $this->pathEncoding));
	}

	/**
	 * 既存のファイルをZIPファイルに追加する
	 * @param string $file 追加するファイル
	 * @param $zippath アーカイブファイル内でのファイルパス、省略された場合は追加するファイル
	 * 		のファイル名を使用してZIPファイルのTOPレベルに格納する
	 */
	public function addFile($file, $zippath = null)
	{
		$file = (string)$file;
		if (! file_exists($file)) {
			$message = 'File dose not found. ' . $file;
			throw new FileNotFoundException($message);
		}

		if ($zippath === null) {
			$zippath = mb_convert_encoding(basename($file), $this->pathEncoding);
		}
		$this->zip->addFile($file, $zippath);
	}

	/**
	 * 文字列からファイルを作成し、ZIPに追加する
	 *
	 * @param string $zippath アーカイブファイル内でのファイルパス
	 * @param string $content ファイルの内容
	 */
	public function addContents($zippath, $content)
	{
		$this->zip->addFromString(mb_convert_encoding($zippath, $this->pathEncoding), $content);
	}

	/**
	 * ストリームを閉じる
	 */
	public function close()
	{
		$this->zip->close();
		$this->zip = null;
	}

}

/**
 * ZIPファイルインプットストリーム
 * @see FileInputStream
 */
class ZipInputStream extends FileInputStream
{
	/**
	 * コンストラクタ
	 * @param resource $fp
	 */
	public function __construct($fp)
	{
		$this->fp = $fp;
	}
}


